/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License.You may obtain a copy of the License at
	http://www.mozilla.org/MPL/MPL-1.1.html
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import fever.structures.color.HSL;
import fever.structures.color.HSLRange;
import fever.structures.color.HSV;
import fever.structures.color.HSVRange;
import fever.structures.color.RGB;

/**
 * Provides methods to play with color structure.
 * 
 * @see fever.structures.IColor
 * @see fever.structures.color.HSV
 * @see fever.structures.color.HSL
 * @see fever.structures.color.RGB
 * 
 * @author Romain Ecarnot
 */
class fever.utils.ColorSpace
{
	//-------------------------------------------------------------------------
	// Constants definition
	//-------------------------------------------------------------------------
	
	/** Constant defines the HSV range used in application. */
	public static var HSVRANGE : HSVRange = HSVRange.PHOTOSHOP;
	
	/** Constant defines the HSL range used in application. */
	public static var HSLRANGE : HSLRange = HSLRange.WINDOWS;
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.RGB} object to 
	 * {@link fever.structures.color.HSL} structure.
	 */
	public static function rgbToHsl( rgb : RGB ) : HSL
	{
		var r : Number = ( rgb.red / 255 );
		var g : Number = ( rgb.green / 255 );
		var b : Number = ( rgb.blue / 255 );
		
		var min : Number = Math.min( Math.min(r, g), b ) ;
		var max : Number = Math.max( Math.max(r, g), b );
		var diff : Number = max - min;
		
		var hue : Number;
		var sat : Number;
		var lum = ( max + min ) / 2;
		
		if ( diff == 0 )
		{
			hue = 0;
			sat = 0;
		}
		else
		{
			if ( lum < 0.5 ) sat = diff / ( max + min );
  			 else  sat = diff / ( 2 - max - min );
			
			var dr = ( ( ( max - r ) / 6 ) + ( diff / 2 ) ) / diff;
			var dg = ( ( ( max - g ) / 6 ) + ( diff / 2 ) ) / diff;
			var db = ( ( ( max - b ) / 6 ) + ( diff / 2 ) ) / diff;
			
			if ( r == max ) hue = db - dg;
			else if ( g == max ) hue = ( 1 / 3 ) + dr - db;
			else if ( b == max ) hue = ( 2 / 3 ) + dg - dr;
			
			if ( hue < 0 ) ; hue += 1;
			if ( hue > 1 ) ; hue -= 1;
		}
		
		return new HSL( 
			Math.round( hue * HSLRANGE.hue.max ), 
			Math.round( sat * HSLRANGE.saturation.max ), 
			Math.round( lum * HSLRANGE.luminance.max )
		);
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.RGB} object to 
	 * {@link fever.structures.color.HSV} structure.
	 */
	public static function rgbToHsv( rgb : RGB ) : HSV
	{
		var r : Number = ( rgb.red / 255 );
		var g : Number = ( rgb.green / 255 );
		var b : Number = ( rgb.blue / 255 );
		
		var min : Number = Math.min( Math.min(r, g), b ) ;
		var max : Number = Math.max( Math.max(r, g), b );
		var del_Max = max - min;
		
		var hue : Number;
		var sat : Number;
		var value = max;
		
		if ( del_Max == 0 )
		{
			hue = 0;
			sat = 0;
		}
		else
		{
			sat = del_Max / max;
			
			var dr = ( ( ( max - r ) / 6 ) + ( del_Max / 2 ) ) / del_Max;
			var dg = ( ( ( max - g ) / 6 ) + ( del_Max / 2 ) ) / del_Max;
			var db = ( ( ( max - b ) / 6 ) + ( del_Max / 2 ) ) / del_Max;
			
			if ( r == max ) hue = db - dg;
			else if ( g == max ) hue = ( 1 / 3 ) + dr - db;
			else if ( b == max ) hue = ( 2 / 3 ) + dg - dr;
			
			if ( hue < 0 ) ; hue += 1;
			if ( hue > 1 ) ; hue -= 1;
		}
		
		return new HSV( 
			Math.round( hue * HSVRANGE.hue.max ), 
			Math.round( sat * HSVRANGE.saturation.max ), 
			Math.round( value * HSVRANGE.value.max )
		);
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.RGB} object to 
	 * hexadecimal number.
	 */
	public static function rgbToHexa( rgb : RGB ) : Number
	{
		return ( (rgb.red << 16) | (rgb.green << 8) | rgb.blue );	
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.RGB} object to 
	 * hexadecimal strong notation.
	 */
	public static function rgbToString( rgb : RGB ) : String
	{
		return hexaToString( rgbToHexa( rgb ) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSL} object to 
	 * {@link fever.structures.color.RGB} structure.
	 */
	public static function hslToRgb( hsl : HSL ) : RGB
	{
		var r : Number;
		var g : Number;
		var b : Number;
		
		var hue : Number = hsl.hue / HSLRANGE.hue.max;
		var sat : Number = hsl.saturation / HSLRANGE.saturation.max;
		var lum : Number = hsl.luminance / HSLRANGE.luminance.max;
		
		if ( sat == 0 )
		{
		   r = lum * 255;
		   g = lum * 255;
		   b = lum * 255;
		}
		else
		{
		   var tmp : Number;
		   if ( lum < 0.5 ) tmp = lum * ( 1 + sat );
		   else tmp = ( lum + sat ) - ( sat * lum );
		
		   var diff : Number = 2 * lum - tmp;
		
		   r = 255 * _hueToRgb( diff, tmp, hue + ( 1 / 3 ) );
		   g = 255 * _hueToRgb( diff, tmp, hue );
		   b = 255 * _hueToRgb( diff, tmp, hue - ( 1 / 3 ) );
		}
		
		return new RGB( Math.round(r), Math.round(g), Math.round(b) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSL} object to 
	 * {@link fever.structures.color.HSV} structure.
	 */
	public static function hslToHsv( hsl : HSL ) : HSV
	{
		return rgbToHsv( hslToRgb( hsl ) );	
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSL} object to 
	 * hexadecimal number.
	 */
	public static function hslToHexa( hsl : HSL ) : Number
	{
		return rgbToHexa( hslToRgb( hsl ) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSL} object to 
	 * hexadecimal string notation.
	 */
	public static function hslToString( hsl : HSL ) : String
	{
		return rgbToString( hslToRgb( hsl ) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSV} object to 
	 * {@link fever.structures.color.RGB} structure.
	 */
	public static function hsvToRgb( hsv : HSV ) : RGB
	{
		var r : Number;
		var g : Number;
		var b : Number;
		
		var hue : Number = hsv.hue / HSVRANGE.hue.max;
		var sat : Number = hsv.saturation / HSVRANGE.saturation.max;
		var value : Number = hsv.value / HSVRANGE.value.max;
		
		if ( sat == 0 )
		{
		   r = value * 255;
		   g = value * 255;
		   b = value * 255;
		}
		else
		{
			var nr : Number;
			var ng : Number;
			var nb : Number;
			var tmpHue = hue * 6;
			
			if ( tmpHue == 6 ) tmpHue = 0;
			
			var i : Number = int( tmpHue );
			var v1 : Number = value * ( 1 - sat );
			var v2 : Number = value * ( 1 - sat * ( tmpHue - i ) );
			var v3 : Number = value * ( 1 - sat * ( 1 - ( tmpHue - i ) ) );
			
			if ( i == 0 )
			{
				 nr = value;
				 ng = v3 ;
				 nb = v1;
			}
			else if ( i == 1 )
			{
				nr = v2;
				ng = value;
				nb = v1;
			}
			else if ( i == 2 )
			{
				nr = v1;
				ng = value;
				nb = v3;
			}
			else if ( i == 3 )
			{
				nr = v1;
				ng = v2;
				nb = value;
			}
			else if ( i == 4 )
			{
				nr = v3;
				ng = v1;
				nb = value;
			}
			else
			{
				nr = value;
				ng = v1;
				nb = v2;
			}
		
		   r = nr * 255;
		   g = ng * 255;
		   b = nb * 255;
		}

		return new RGB( Math.round(r), Math.round(g), Math.round(b) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSV} object to 
	 * {@link fever.structures.color.HSL} structure.
	 */
	public static function hsvToHsl( hsv : HSV ) : HSL
	{
		return rgbToHsl( hsvToRgb( hsv ) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSV} object to 
	 * hexadecimal number.
	 */
	public static function hsvToHexa( hsv : HSV ) : Number
	{
		return rgbToHexa( hsvToRgb( hsv ) );
	}
	
	/**
	 * Transforms the passed-in {@link fever.structures.color.HSV} object to 
	 * hexadecimal string notation.
	 */
	public static function hsvToString( hsv : HSV ) : String
	{
		return rgbToString( hsvToRgb( hsv ) );
	}
	
	/**
	 * Transforms the passed-in number to {@link fever.structures.color.RGB} 
	 * instance.
	 */
	public static function hexaToRgb( hexa : Number ) : RGB
	{
		var r : Number = hexa >> 16;
		var gb : Number = hexa ^ r << 16;
		var g : Number = gb >> 8;
		var b : Number = gb ^ g << 8;
		
		return new RGB( r, g, b );
	}
	
	/**
	 * Transforms the passed-in number to {@link fever.structures.color.HSL} 
	 * instance.
	 */
	public static function hexaToHsl( hexa : Number ) : HSL
	{
		return rgbToHsl( hexaToRgb( hexa ) );	
	}
	
	/**
	 * Transforms the passed-in number to {@link fever.structures.color.HSV} 
	 * instance.
	 */
	public static function hexaToHsv( hexa : Number ) : HSV
	{
		return rgbToHsv( hexaToRgb( hexa ) );	
	}
	
	/**
	 * Transforms the passed-in number to hexadecimal string notation.
	 */
	public static function hexaToString( hexa : Number ) : String
	{
		var s : String = hexa.toString(16);
		var l : Number = 6 - s.length;
		while (l--) s = "0" + s;
		return s.toUpperCase();	
	}

	/**
	 * Transforms the passed-in string to number.
	 */
	public static function stringToHexa( str : String ) : Number
	{
		str = str.substr( -6, 6 );
		return parseInt( str, 16 );	
	}
	
	/**
	 * Transforms the passed-in string to {@link fever.structures.color.RGB} 
	 * instance.
	 */
	public static function stringToRgb( str : String ) : RGB
	{
		return hexaToRgb( stringToHexa( str) );
	}
	
	/**
	 * Transforms the passed-in string to {@link fever.structures.color.HSL} 
	 * instance.
	 */
	public static function stringToHsl( str : String ) : HSL
	{
		return hexaToHsl( stringToHexa( str ) );
	}
	
	/**
	 * Transforms the passed-in string to {@link fever.structures.color.HSV} 
	 * instance.
	 */
	public static function stringToHsv( str : String ) : HSV
	{
		return hexaToHsv( stringToHexa( str ) );
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	private static function _hueToRgb( diff : Number, sat : Number, hue : Number ) : Number
	{
	   if ( hue < 0 ) hue += 1;
	   if ( hue > 1 ) hue -= 1;
	   if ( ( 6 * hue ) < 1 ) return ( diff + ( sat - diff ) * 6 * hue );
	   if ( ( 2 * hue ) < 1 ) return ( sat );
	   if ( ( 3 * hue ) < 2 ) return ( diff + ( sat - diff ) * ( ( 2 / 3 ) - hue ) * 6 );
	   return ( diff );
	}
	
	
	private function ColorSpace() { }	
}